/*
 * Copyright (C) 2017  即时通讯网(52im.net) & Jack Jiang.
 * The MobileIMSDK_X_netty (MobileIMSDK v3.x Netty版) Project.
 * All rights reserved.
 *
 * > Github地址: https://github.com/JackJiang2011/MobileIMSDK
 * > 文档地址: http://www.52im.net/forum-89-1.html
 * > 即时通讯技术社区：http://www.52im.net/
 * > 即时通讯技术交流群：320837163 (http://www.52im.net/topic-qqgroup.html)
 *
 * "即时通讯网(52im.net) - 即时通讯开发者社区!" 推荐开源工程。
 *
 * QoS4ReciveDaemonRoot.java at 2017-12-9 11:24:33, code by Jack Jiang.
 * You can contact author with jack.jiang@52im.net or jb2011@163.com.
 */
package com.srwl.mytx.im.core;

import android.util.Log;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 消息接收队列
 * 收到的需要普通消息全部加进来，接收消息之前判断是否是重复消息，如果是，丢弃
 * 定时删除超过给定有效期的历史消息
 */
@SuppressWarnings("ALL")
public class MessageReceivedQueue {
    private static final String TAG = "MessageReceivedQueue";

    private static MessageReceivedQueue instance = null;

    public static MessageReceivedQueue getInstance() {
        if (instance == null)
            instance = new MessageReceivedQueue();
        return instance;
    }


    /**
     * 检查线程执行间隔（单位：毫秒），默认5分钟
     */
    private int CHECH_INTERVAL = 5 * 60 * 1000; // 5分钟

    /**
     * 一个消息放到在列表中（用于判定重复时使用）的生存时长（单位：毫秒），默认10分钟
     */
    private int MESSAGES_VALID_TIME = 10 * 60 * 1000; // 10分钟

    /**
     * 存储收到的消息id 和收到的时间
     * key=消息包指纹码，value=最近1次收到该包的时间戳（时间戳用于判定该包是否已失效时有
     * 用，收到重复包时用最近一次收到时间更新时间戳从而最大限度保证不重复）
     */
    private static ConcurrentMap<String, Long> recievedMessages = new ConcurrentHashMap<String, Long>();

    private Timer timer = null;

    private boolean excuting = false;


    private void clearReceivedMessageTask() {
        // 极端情况下本次循环内可能执行时间超过了时间间隔，此处是防止在前一
        // 次还没有运行完的情况下又重复执行，从而出现无法预知的错误
        if (!excuting) {
            excuting = true;

            Log.d(TAG, "clearReceivedMessageTask: 暂存处理线程正在运行中，当前长度" + recievedMessages.size());
            Iterator<Entry<String, Long>> entryIt = recievedMessages.entrySet().iterator();
            while (entryIt.hasNext()) {
                Entry<String, Long> entry = entryIt.next();
                String key = entry.getKey();
                long value = entry.getValue();

                long delta = System.currentTimeMillis() - value;
                // 该消息包超过了生命时长，去掉之
                if (delta >= MESSAGES_VALID_TIME) {
                    Log.d(TAG, "clearReceivedMessageTask:超过最大存活时间，清除掉  messageId: " + key);
                    recievedMessages.remove(key);
                }
            }
        }

        excuting = false;
    }

    /**
     * 启动线程。
     * <p>
     * 无论本方法调用前线程是否已经在运行中，都会尝试首先调用 {@link #stop()}方法，
     * 以便确保线程被启动前是真正处于停止状态，这也意味着可无害调用本方法。
     * <p>
     * <b>本线程的启停，目前属于MobileIMSDK算法的一部分，暂时无需也不建议由应用层自行调用。</b>
     */
    public void startTask() {
        // ** 先确保之前定时任务被停止
        stop();

        // ** 如果列表不为空则尝试重置生成起始时间
        // 当重启时列表可能是不为空的（此场景也许是出现暂停此类的定时器后又重新启动时），
        // 那么为了防止在此情况下，客户端可能存在的重传，此时机下也能一定程度避免消息重复的可能
        // ，因为客户端在网络不好时会暂停重传直到网络恢复又继续（不可此情况下服务端的防重复机制也
        // 确实很难绝对的起作用，因为客户端恢复重传时可能已过去很久了，不过此种几率应该非常小）
        if (recievedMessages != null && recievedMessages.size() > 0) {
            for (String key : recievedMessages.keySet()) {
                // 重置列表中的生存起始时间
                addInQueue(key);
            }
        }

        // ** 启动定时任务
        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
                                      @Override
                                      public void run() {
                                          clearReceivedMessageTask();
                                      }
                                  }
                // 首次执行时的延迟
                , CHECH_INTERVAL
                // 之后每次执行时的固定间隔时间（使用scheduleAtFixedRate而不
                // 是schedule目的是希望以真正的固定间隔、而非固定延迟）
                , CHECH_INTERVAL);
    }

    /**
     * 无条件中断本线程的运行。
     * <p>
     * <b>本线程的启停，目前属于MobileIMSDK算法的一部分，暂时无需也不建议由应用层自行调用。</b>
     */
    public void stop() {
        if (timer != null) {
            try {
                timer.cancel();
            } finally {
                timer = null;
            }
        }
    }


    public void addInQueue(String messageId) {
        if (messageId != null)
            recievedMessages.put(messageId, System.currentTimeMillis());
    }


    public boolean isRepeatMessage(String messageId) {
        return recievedMessages.containsKey(messageId);
    }

    /**
     * 当前“已收到消息”队列列表的大小.
     *
     * @return
     * @see ArrayList#size()
     */
    public int size() {
        return recievedMessages.size();
    }


}
